package org.dce.screencapturer.objects;

import java.awt.AWTEvent;
import java.awt.Insets;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent;

@SuppressWarnings("serial")
public abstract class DotBasedShapeObject 
	extends ShapeObject{

	private final ComponentListener dotListener = new ComponentAdapter() {
		
		boolean resizing = false;
		
		public void componentMoved(ComponentEvent event){
			if (!resizing){
				resizing = true;
				updateBoundsOnDotMovement();
				notifyDotMoved((Dot)event.getComponent());
				resizing = false;
			}
		}
		
	};
	
	protected void updateBoundsOnDotMovement(){
		
		Dot[] dots = getDots();
		
		int minX = minX(dots);
		int minY = minY(dots);
		int maxX = maxX(dots);
		int maxY = maxY(dots);
		
		int xoffset = 0;
		int yoffset = 0;
		
		int extraWidth = 0;
		int extraHeight = 0;

		xoffset = -minX;
		yoffset = -minY;
		
		extraWidth = maxX - getWidth();
		extraHeight = maxY - getHeight();
		
		Insets insets = getShapeAttributes().getInsets();
		
		xoffset-=insets.left;
		yoffset-=insets.top;
		extraWidth+=insets.left+insets.right;
		extraHeight+=insets.top+insets.bottom;
		
		setLocation(getX()-xoffset, getY()-yoffset);
		setSize(getWidth()+xoffset+extraWidth,getHeight()+yoffset+extraHeight);
		
		for (Dot dot:dots){
			dot.setLocation(dot.getX()+xoffset, dot.getY()+yoffset);
		}
		rebuildShape();
		repaint();
	}
	
	public DotBasedShapeObject(){
		enableEvents(AWTEvent.COMPONENT_EVENT_MASK);
	}
	
	protected void notifyDotMoved(Dot component) {
	}

	protected void initDots(Dot ... dots){
		for (Dot dot:dots){
			dot.addComponentListener(dotListener);
			add(dot);
		}
	}

	protected void processFocusEvent(FocusEvent event){
		
		boolean gained = event.getID() == FocusEvent.FOCUS_GAINED;
		
		setDotsVisible(gained);
		
		super.processFocusEvent(event);
	}

	@Override
	public boolean contains(int x, int y) {
		if (super.contains(x, y)){
			return true;
		}
		
		for (Dot dot:getDots()){
			if (dot.contains(x-dot.getX(), y-dot.getY())){
				return true;
			}
		}
		return false;
	}


	private void setDotsVisible(boolean visible) {
		for (Dot dot:getDots()){
			dot.setVisible(visible);
		}
	}
	
	private int minX(Dot ... dots){
		int min = Integer.MAX_VALUE;
		for (Dot dot:dots){
			min = Math.min(dot.getX(), min);
		}
		return min;
	}

	private int minY(Dot ... dots){
		int min = Integer.MAX_VALUE;
		for (Dot dot:dots){
			min = Math.min(dot.getY(), min);
		}
		return min;
	}

	private int maxX(Dot ... dots){
		int max = Integer.MIN_VALUE;
		for (Dot dot:dots){
			max = Math.max(dot.getLeftX(), max);
		}
		return max;
	}

	private int maxY(Dot ... dots){
		int max = Integer.MIN_VALUE;
		for (Dot dot:dots){
			max = Math.max(dot.getBottomY(), max);
		}
		return max;
	}

	protected abstract Dot[] getDots();

}
